﻿/*
 * Copyright (C) 2011, EADS France
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * Author: Laurent WOUTERS
 */

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Hime.Redist.Parsers;

namespace xowl.gmi.compiler.ast
{
    class ASTPattern
    {
        protected static int routeCount = 0;
        protected static string GetNextRoute()
        {
            string next = "route" + routeCount.ToString();
            routeCount++;
            return next;
        }
        protected static Random rand = new Random();

        protected string symbol;
        protected string route;
        protected List<ASTPattern> children;
        protected List<Parameter> parameters;

        public string Symbol { get { return symbol; } }
        public string Route { get { return route; } }
        public bool HasChildren { get { return (children.Count != 0); } }
        public ICollection<Parameter> Parameters { get { return parameters; } }

        public ASTPattern(string symbol)
        {
            this.symbol = symbol;
            this.children = new List<ASTPattern>();
            this.parameters = new List<Parameter>();
        }

        public void AddChild(ASTPattern child) { children.Add(child); }
        public void AddParameter(Parameter parameter) { parameters.Add(parameter); }

        public virtual List<ASTPattern> Split()
        {
            List<ASTPattern> result = new List<ASTPattern>();
            if (children.Count == 0)
            {
                result.Add(this);
            }
            else
            {
                List<List<ASTPattern>> temp = SplitChildren();
                foreach (List<ASTPattern> choice in temp)
                {
                    ASTPattern parent = new ASTPattern(symbol);
                    parent.children.AddRange(choice);
                    result.Add(parent);
                }
            }
            return result;
        }

        protected List<List<ASTPattern>> SplitChildren()
        {
            List<List<ASTPattern>> temp = new List<List<ASTPattern>>();
            foreach (ASTPattern child in children)
                temp.Add(child.Split());
            List<List<ASTPattern>> result = new List<List<ASTPattern>>();
            foreach (ASTPattern node in temp[0])
            {
                List<ASTPattern> choice = new List<ASTPattern>();
                choice.Add(node);
                result.Add(choice);
            }
            for (int i = 1; i != temp.Count; i++)
                multiply(result, temp[i]);
            return result;
        }
        protected void multiply(List<List<ASTPattern>> left, List<ASTPattern> right)
        {
            if (right.Count == 0)
                return;
            if (right.Count == 1)
            {
                foreach (List<ASTPattern> choice in left)
                    choice.Add(right[0]);
                return;
            }
            List<List<ASTPattern>> temp = new List<List<ASTPattern>>();
            foreach (ASTPattern r in right)
            {
                foreach (List<ASTPattern> choice in left)
                {
                    List<ASTPattern> m = new List<ASTPattern>(choice);
                    m.Add(r);
                    temp.Add(m);
                }
            }
            left.Clear();
            left.AddRange(temp);
        }

        public List<ASTPattern> GetVariableNodes()
        {
            List<ASTPattern> result = new List<ASTPattern>();
            BuildVariableNodesList(result);
            return result;
        }

        public virtual void BuildVariableNodesList(List<ASTPattern> result)
        {
            foreach (ASTPattern child in children)
                child.BuildVariableNodesList(result);
        }

        public virtual void BuildRoutes()
        {
            foreach (ASTPattern child in children)
            {
                if (child.NeedRoute())
                {
                    if (route == null)
                        route = GetNextRoute();
                    child.SetParentRoute(route);
                }
                child.BuildRoutes();
            }
        }
        public virtual bool NeedRoute() { return false; }
        public virtual void SetParentRoute(string route) { }

        public MinimalAST GetMinimal()
        {
            MinimalAST min = new MinimalAST(symbol, route);
            foreach (ASTPattern child in children)
            {
                MinimalAST cm = child.GetMinimalInner();
                if (cm != null)
                    min.AddChild(cm);
            }
            return min;
        }
        public virtual MinimalAST GetMinimalInner()
        {
            MinimalAST min = new MinimalAST(symbol, route);
            if (parameters.Count != 0)
                min.AddParameters(this.parameters);
            foreach (ASTPattern child in children)
            {
                MinimalAST cm = child.GetMinimalInner();
                if (cm != null)
                    min.AddChild(cm);
            }
            return min;
        }

        public List<Cascade> GetCascades()
        {
            List<Cascade> results = new List<Cascade>();
            GetCascadesInner(results, null);
            return results;
        }
        private void GetCascadesInner(List<Cascade> cascades, ASTPattern parent)
        {
            foreach (Parameter param in parameters)
                if (param.ValueType == ParameterValueType.Parameter)
                    cascades.Add(new Cascade(parent.symbol, this.symbol, param.Value.Substring(1), param.Name));
            foreach (ASTPattern child in children)
                child.GetCascadesInner(cascades, this);
        }

        public override bool Equals(object obj)
        {
            ASTPattern node = obj as ASTPattern;
            if (node == null)
                return false;
            return NodeEquals(node);
        }
        protected bool NodeEquals(ASTPattern tested)
        {
            if (this.symbol != tested.symbol)
                return false;
            if (this.children.Count != tested.children.Count)
                return false;
            for (int i = 0; i != this.children.Count; i++)
                if (!this.children[i].Equals(tested.children[i]))
                    return false;
            return true;
        }
        public override int GetHashCode() { return base.GetHashCode(); }
    }
}
